package wowrl.engine;

import java.awt.Point;

import wowrl.data.ItemFactory;
import wowrl.data.MobFactory;
import wowrl.data.Terrain;
import wowrl.data.TerrainFactory;
import wowrl.data.WOWRL_Data;
import wowrl.data.Terrain.TerrainType;

public class DungeonGenerator {
	private WOWRL_Data gameState;
	private TerrainFactory terrainFactory;
	private ItemFactory itemFactory;
	private MobFactory mobFactory;
	
	private enum Direction
	{
		North,
		South,
		East,
		West;
	}
	
	public DungeonGenerator(WOWRL_Data gameState, TerrainFactory terrainFactory, ItemFactory itemFactory, MobFactory mobFactory )
	{
		this.gameState = gameState;
		this.terrainFactory = terrainFactory;
		this.itemFactory = itemFactory;
		this.mobFactory = mobFactory;

		InitializeDungeon();
	}
	
	private void InitializeDungeon(){
		FillDungeon(terrainFactory.createVoid());
	}
	
	public void FillDungeon(Terrain terrain){
		//Populate gameMap with default (blank) data
		for (int x=0; x<=WOWRL_Data.MAX_X; x++){
			for (int y=0; y<=WOWRL_Data.MAX_Y; y++){			
				gameState.map.terrain[x][y] = terrain;
			}
		}
	}
	
	public void CreateDungeon(){
		CreateTestRoom(true);
		//CreateTestForest();
	}
	
	private void CreateTestForest()
	{
		CreateRoom(new Point(0,0), new Point(WOWRL_Data.MAX_X,WOWRL_Data.MAX_Y),false);
		
		int xLocation = 1;
		int yLocation = 1;
		
		//TEST - Static starting location
		gameState.setPlayerLocation(new Point(xLocation+2,yLocation+2));
		
		//TEST - create an item (apple)
		gameState.map.items[xLocation+3][yLocation+3] = itemFactory.createApple();
		//gameState.map.items[5][5].add(itemFactory.createApple());
		
		//TEST - create a monster (rat)
		gameState.map.mobs[xLocation+4][yLocation+4] = mobFactory.createRat();
		
	}
	private void CreateTestRoom(Boolean startingRoom)
	{
		//Determine room size
		int xSize = Dice.RandomNumberRange(6,maxRoomSize-2);
		int ySize = Dice.RandomNumberRange(6,maxRoomSize-2);
	
		//Determine room location
		int xLocation = Dice.RandomNumberRange(0,WOWRL_Data.MAX_X-xSize);
		int yLocation = Dice.RandomNumberRange(0,WOWRL_Data.MAX_Y-ySize);
		
		//Create the room
		CreateRoom(new Point(xLocation,yLocation), new Point(xLocation+xSize+1,yLocation+ySize+1),true);
		
		//TEST - Static starting location
		gameState.setPlayerLocation(new Point(xLocation+2,yLocation+2));
		
		//TEST - create an item (apple)
		gameState.map.items[xLocation+3][yLocation+3] = itemFactory.createApple();
		//gameState.map.items[5][5].add(itemFactory.createApple());
		
		//TEST - create a monster (rat)
		gameState.map.mobs[xLocation+4][yLocation+4] = mobFactory.createRat();		
	}
	
	private int minRoomSize = 4; //2x2
	private int maxRoomSize = 12;
	private int minCorridorSize = 3; //To pierce walls and still have a square that is all corridor. Could be set to 1 but results of 2 will look ugly
	
	private Direction RandomDirection()
	{
		int picker = (int)(Math.random() * 4);
		switch (picker)
		{
			case 0:
				return Direction.North;	
			case 1:
				return Direction.South;
			case 2:
				return Direction.West;
			case 3:
				return Direction.East;
			default:
				return Direction.North; //Should never occur
		}
	}
	private Boolean CreateCorridor(Point startPoint, Direction direction, Boolean branch)
	{
		Point endLocation = startPoint;
		int startLength = 0;
		int maxLength = 0;
		int actualLength;
		
		switch (direction)
		{
			case North:
				startLength = startPoint.y-1;
				for (maxLength=startLength; maxLength>=WOWRL_Data.MIN_Y; maxLength--)
				{ if (gameState.map.terrain[startPoint.x][maxLength].type != TerrainType.Void)	{ break; }	}
				break;				
			case South:
				startLength = startPoint.y+1;				
				for (maxLength=startLength; maxLength<=WOWRL_Data.MAX_Y; maxLength++)
				{ if (gameState.map.terrain[startPoint.x][maxLength].type != TerrainType.Void)	{ break; }	}
				break;				
			case West:
				startLength = startPoint.x-1;
				for (maxLength=startLength; maxLength>=WOWRL_Data.MIN_X; maxLength--)
				{ if (gameState.map.terrain[maxLength][startPoint.y].type != TerrainType.Void)	{ break; }	}
				break;				
			case East:
				startLength = startPoint.x+1;
				for (maxLength=startLength; maxLength<=WOWRL_Data.MAX_X; maxLength++)
				{ if (gameState.map.terrain[maxLength][startPoint.y].type != TerrainType.Void)	{ break; }	}
				break;
			default:				
				break;				
		}
		
		maxLength = Math.abs(maxLength - startLength);
		if (maxLength < minRoomSize + minCorridorSize) { return false;}
		
		//Set length to somewhere within the allowable area, minus the size of the Room
		actualLength = (minCorridorSize) + (int)(Math.random()*(maxLength-(minRoomSize + minCorridorSize)));
		
		/* CREATE THE ACTUAL CORRIDOR */
		switch (direction)
		{
			case North:
				endLocation = new Point(startPoint.x,startPoint.y-actualLength);
				for (int y = startPoint.y; y >= endLocation.y ; y--)
				{
					gameState.map.terrain[startPoint.x-1][y] = terrainFactory.createWall();
					gameState.map.terrain[startPoint.x][y] = terrainFactory.createFloor();
					gameState.map.terrain[startPoint.x+1][y] = terrainFactory.createWall();
				}
				break;
			case South:
				endLocation = new Point(startPoint.x,startPoint.y+actualLength);
				for (int y = startPoint.y; y <= endLocation.y ; y++)
				{
					gameState.map.terrain[startPoint.x-1][y] = terrainFactory.createWall();
					gameState.map.terrain[startPoint.x][y] = terrainFactory.createFloor();		
					gameState.map.terrain[startPoint.x+1][y] = terrainFactory.createWall();
				}
				break;
			case West:
				endLocation = new Point(startPoint.x-actualLength,startPoint.y);
				for (int x = startPoint.x; x >= endLocation.x ; x--)
				{
					gameState.map.terrain[x][startPoint.y-1] = terrainFactory.createWall();
					gameState.map.terrain[x][startPoint.y] = terrainFactory.createFloor();	
					gameState.map.terrain[x][startPoint.y+1] = terrainFactory.createWall();
				}
				break;
			case East:
				endLocation = new Point(startPoint.x+actualLength,startPoint.y);
				for (int x = startPoint.x; x <= endLocation.x ; x++)
				{
					gameState.map.terrain[x][startPoint.y-1] = terrainFactory.createWall();
					gameState.map.terrain[x][startPoint.y] = terrainFactory.createFloor();	
					gameState.map.terrain[x][startPoint.y+1] = terrainFactory.createWall();
				}
				break;
			default:
				
				break;				
		}
		
		//Create a room at the end of the corridor
		CreateRoomOnCorridor(endLocation,direction, branch);
		
		return true;
	}
	
	public void CreateRoomOnCorridor(Point corridorPoint, Direction direction, Boolean branch)
	{	
		int left,right,top,bottom;
		switch (direction)
		{
			case North:
				left = Dice.RandomNumberRange(Math.max(WOWRL_Data.MIN_X,corridorPoint.x-maxRoomSize), corridorPoint.x-2);
				right = Dice.RandomNumberRange(corridorPoint.x+2, Math.min(WOWRL_Data.MAX_X,corridorPoint.x+maxRoomSize));				
				top = Dice.RandomNumberRange(Math.max(WOWRL_Data.MIN_Y,corridorPoint.y-maxRoomSize), corridorPoint.y-minRoomSize);
				bottom = corridorPoint.y;
				CreateRoom(new Point(left,top),new Point(right,bottom),branch);
				break;
			case South:
				left = Dice.RandomNumberRange(Math.max(WOWRL_Data.MIN_X,corridorPoint.x-maxRoomSize), corridorPoint.x-2);
				right = Dice.RandomNumberRange(corridorPoint.x+2, Math.min(WOWRL_Data.MAX_X,corridorPoint.x+maxRoomSize));						
				top = corridorPoint.y;
				bottom = Dice.RandomNumberRange(corridorPoint.y+minRoomSize, Math.min(WOWRL_Data.MAX_Y,corridorPoint.y+maxRoomSize));
				CreateRoom(new Point(left,top),new Point(right,bottom),branch);
				break;
			case West:
				left = Dice.RandomNumberRange(Math.max(WOWRL_Data.MIN_X,corridorPoint.x-maxRoomSize), corridorPoint.x-minRoomSize);
				right = corridorPoint.x;
				top = Dice.RandomNumberRange(Math.max(WOWRL_Data.MIN_Y,corridorPoint.y-maxRoomSize), corridorPoint.y-2);
				bottom = Dice.RandomNumberRange(corridorPoint.y+2, Math.min(WOWRL_Data.MAX_Y,corridorPoint.y+maxRoomSize));	
				CreateRoom(new Point(left,top),new Point(right,bottom),branch);
				break;
			case East:
				left = corridorPoint.x;
				right = Dice.RandomNumberRange(corridorPoint.x+minRoomSize, Math.min(WOWRL_Data.MAX_X,corridorPoint.x+maxRoomSize));
				top = Dice.RandomNumberRange(Math.max(WOWRL_Data.MIN_Y,corridorPoint.y-maxRoomSize), corridorPoint.y-2);
				bottom = Dice.RandomNumberRange(corridorPoint.y+2, Math.min(WOWRL_Data.MAX_Y,corridorPoint.y+maxRoomSize));	
				CreateRoom(new Point(left,top),new Point(right,bottom),branch);
				break;
		}
		
		
	}
	
	int corridorsMade = 0;
	
	public void CreateRoom(Point topLeft, Point bottomRight, Boolean branch)
	{
		//Create the floor and walls
		for (int x=topLeft.x;x<=bottomRight.x;x++){
			for (int y=topLeft.y;y<=bottomRight.y;y++)
			{	
				if (gameState.validateLocation(new Point(x,y)))
				{
					//Check if this is the edge of the room
					if (x==topLeft.x || x==bottomRight.x || y==topLeft.y || y==bottomRight.y){
						//Only replace solid objects?
						if (gameState.map.terrain[x][y].isSolid)
						{
							//If so, put a wall
							gameState.map.terrain[x][y] = terrainFactory.createWall();
						}
					}
					else
					{
						//Otherwise floor
						gameState.map.terrain[x][y] = terrainFactory.createFloor();
					}
				}
			}
		}
				
		if (branch && corridorsMade < 3)
		{
			corridorsMade++;
			CreateCorridor(new Point(topLeft.x+1,topLeft.y), Direction.North, branch);
			CreateCorridor(new Point(topLeft.x+1,bottomRight.y), Direction.South, branch);
			CreateCorridor(new Point(topLeft.x,topLeft.y+1), Direction.West, branch);
			CreateCorridor(new Point(bottomRight.x,topLeft.y+1), Direction.East, branch);	
		}
	}
}
